﻿package ygg.encoding.amf
{
	import flash.utils.ByteArray;
	import flash.utils.describeType;
	import flash.utils.Endian;
	import j.sus2maj7.util.t;
	import flash.utils.*;
	
	import j.sus2maj7.encoding.*;
	import ygg.encoding.amf.*;
	import ygg.encoding.*;
	
	public class AmfCodec extends AbstractCodec
	{
		public function AmfCodec()
		{
			super();
		}
		
		override public function decode(binary:IBinary, context:IContext=null):*
		{
			trace('decode');
			
			binary.position = 0;
			binary.endian = Endian.BIG_ENDIAN;
			
			var o:Amf = new Amf();
			o.header = decodeHeader( AmfBinary(binary) );
			
			o.body = decodeBody(binary, o);
			
			return o;
			
		}
		
		// head is same for both amf0 and amf3
		protected function decodeHeader(b:AmfBinary):AmfHeader
		{
			trace('decodeHead');
			
			var head:uint = b.readUnsignedShort(); //00 BF
			
			if ( head != AmfConstants.MAGIC_NUMBER )
				throw new Error("Invalid .sol");
			
			var numBytes:uint = b.readUnsignedInt();
			
			if ( numBytes != b.bytesAvailable )
				throw new Error("length mismatch " + numBytes + " " + b.bytesAvailable);
			
			var signature:String = b.readUTFBytes(10); //TCSO
			
			if (signature != AmfConstants.SIGNATURE)
				throw new Error("invalid signature");
			
			var nameLen:uint = b.readUnsignedShort();
			trace( numBytes, nameLen);
			var name:String = b.readUTFBytes(nameLen);
			
			var version:uint = b.readUnsignedInt();
			
			if (version != AmfVersion.V3 && version != AmfVersion.V0)
				throw new Error("invalid version");
			
			return new AmfHeader(name, version, numBytes);
		}
		protected function decodeBody(b:IBinary, o:Object):Object
		{
			trace('decodeBody');
			
			var context:AmfContext = new AmfContext();
			
			var retVal:Object = { };
			
			context.version = o.header.version;
			context.codecFactory = new AmfCodecFactory();
			context.nameCodec = AmfCodecFactory(context.codecFactory).getStringCodec(context.version);
			
			var name:String;
			var m:uint;
			var value:*;
			
			while (b.bytesAvailable > 0)
			{
				trace('\n');
				
				name = context.nameCodec.decode(b, context);
				trace('name=', name);
				
				m = b.readUnsignedByte();
				trace('marker=', m);
				
				value = context.codecFactory.getDecoder(m, context).decode(b, context);
				trace('value=', value);
				
				retVal[name] = value;
				
				if (b.readByte() != 0x00)
					throw new Error("Missing Padding");
			}
			
			return retVal;
		}
		
		override public function encode(o:*, context:IContext=null):IBinary
		{
			trace('encode', o);
			
			
			var b:AmfBinary = new AmfBinary();
			b.endian = Endian.BIG_ENDIAN;
			
			var body:IBinary = encodeBody(o.body);
			
			encodeHeader(b, o.header, body.length);
			
			b.writeBytes(body.ba);
			
			return b;
			
		}
		
		public function encodeBody(bodyObject:Object):IBinary
		{
			trace('encodeBody');
			
			var body:IBinary = new AmfBinary();
			
			var context:AmfContext = new AmfContext();
			
			context.version = 3;//_o.header.version ||
			context.codecFactory = new AmfCodecFactory();
			context.nameCodec = AmfCodecFactory(context.codecFactory).getStringCodec(context.version);
			
			//loop through object
			for (var i:* in bodyObject)
			{
				trace('\n');
				trace('name=', i);
				body.writeBytes( context.nameCodec.encode(i, context).ba, 1) ;
				
				trace('value=', bodyObject[i]);
				body.writeBytes( context.codecFactory.getEncoder(bodyObject[i], context).encode(bodyObject[i], context).ba );
				
				body.writeByte(0x00); //padding byte
			}
			
			return body;
		}
		
		// head is same for both amf0 and amf3
		protected function encodeHeader(b:IBinary, header:AmfHeader, bodyLen:int):void
		{
			trace('encodeHeader');
			
			//var head:uint = b.readUnsignedShort();
			b.writeShort(AmfConstants.MAGIC_NUMBER); //00 BF //magic number
			//b.writeByte(0xBF);
			
			//var numBytes:uint = b.readUnsignedInt(); //get this from body
			b.writeUnsignedInt(bodyLen + 10 + 1 + 1 + header.name.length + 4);
			
			//var fileType:String = b.readUTFBytes(4); //TCSO
			b.writeUTFBytes(AmfConstants.SIGNATURE);
			b.writeShort(4);
			b.writeInt(0);
			
			
			//var nameLen:uint = b.readUnsignedShort();
			b.writeShort(header.name.length);
			
			//trace(head, numBytes, fileType, nameLen);
			//var name:String = b.readUTFBytes(nameLen);
			b.writeUTFBytes(header.name);
			
			b.writeUnsignedInt(header.version);
			
		}
	}
}